{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 目的：用tensorflow来完成一个在批量数据上更新，并且可以增量迭代优化的矩阵分解推荐系统"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 矩阵分解\n",
    "![](svd_recommendation.png)\n",
    "LFM：把用户再item上打分的行为，看作是有内部依据的，认为和k个factor有关系<br>\n",
    "每一个user i会有一个用户的向量(k维)，每一个item会有一个item的向量(k维)\n",
    "\n",
    "SVD是矩阵分解的一种方式\n",
    "\n",
    "### 预测公式如下\n",
    "$y_{pred[u, i]} = bias_{global} + bias_{user[u]} + bias_{item_[i]} + <embedding_{user[u]}, embedding_{item[i]}>$\n",
    "\n",
    "### 我们需要最小化的loss计算如下（添加正则化项）\n",
    "$\\sum_{u, i} |y_{pred[u, i]} - y_{true[u, i]}|^2 + \\lambda(|embedding_{user[u]}|^2 + |embedding_{item[i]}|^2)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "def read_data_and_process(filname, sep=\"\\t\"):\n",
    "    col_names = [\"user\", \"item\", \"rate\", \"st\"]\n",
    "    df = pd.read_csv(filname, sep=sep, header=None, names=col_names, engine='python')\n",
    "    df[\"user\"] -= 1\n",
    "    df[\"item\"] -= 1\n",
    "    for col in (\"user\", \"item\"):\n",
    "        df[col] = df[col].astype(np.int32)\n",
    "    df[\"rate\"] = df[\"rate\"].astype(np.float32)\n",
    "    return df\n",
    "\n",
    "\n",
    "class ShuffleDataIterator(object):\n",
    "    \"\"\"\n",
    "    随机生成一个batch一个batch数据\n",
    "    \"\"\"\n",
    "    #初始化\n",
    "    def __init__(self, inputs, batch_size=10):\n",
    "        self.inputs = inputs\n",
    "        self.batch_size = batch_size\n",
    "        self.num_cols = len(self.inputs)\n",
    "        self.len = len(self.inputs[0])\n",
    "        self.inputs = np.transpose(np.vstack([np.array(self.inputs[i]) for i in range(self.num_cols)]))\n",
    "\n",
    "    #总样本量\n",
    "    def __len__(self):\n",
    "        return self.len\n",
    "\n",
    "    def __iter__(self):\n",
    "        return self\n",
    "\n",
    "    #取出下一个batch\n",
    "    def __next__(self):\n",
    "        return self.next()\n",
    "    \n",
    "    #随机生成batch_size个下标，取出对应的样本\n",
    "    def next(self):\n",
    "        ids = np.random.randint(0, self.len, (self.batch_size,))\n",
    "        out = self.inputs[ids, :]\n",
    "        return [out[:, i] for i in range(self.num_cols)]\n",
    "\n",
    "\n",
    "class OneEpochDataIterator(ShuffleDataIterator):\n",
    "    \"\"\"\n",
    "    顺序产出一个epoch的数据，在测试中可能会用到\n",
    "    \"\"\"\n",
    "    def __init__(self, inputs, batch_size=10):\n",
    "        super(OneEpochDataIterator, self).__init__(inputs, batch_size=batch_size)\n",
    "        if batch_size > 0:\n",
    "            self.idx_group = np.array_split(np.arange(self.len), np.ceil(self.len / batch_size))\n",
    "        else:\n",
    "            self.idx_group = [np.arange(self.len)]\n",
    "        self.group_id = 0\n",
    "\n",
    "    def next(self):\n",
    "        if self.group_id >= len(self.idx_group):\n",
    "            self.group_id = 0\n",
    "            raise StopIteration\n",
    "        out = self.inputs[self.idx_group[self.group_id], :]\n",
    "        self.group_id += 1\n",
    "        return [out[:, i] for i in range(self.num_cols)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型搭建\n",
    "用tensorflow去搭建一个可增量训练的矩阵分解模型，完成基于矩阵分解的推荐系统"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "\n",
    "import tensorflow as tf\n",
    "\n",
    "# 使用矩阵分解搭建的网络结构\n",
    "def inference_svd(user_batch, item_batch, user_num, item_num, dim=5, device=\"/cpu:0\"):\n",
    "    #使用CPU\n",
    "    with tf.device(\"/cpu:0\"):\n",
    "        # 初始化几个bias项\n",
    "        global_bias = tf.get_variable(\"global_bias\", shape=[])\n",
    "        w_bias_user = tf.get_variable(\"embd_bias_user\", shape=[user_num])\n",
    "        w_bias_item = tf.get_variable(\"embd_bias_item\", shape=[item_num])\n",
    "        # bias向量\n",
    "        bias_user = tf.nn.embedding_lookup(w_bias_user, user_batch, name=\"bias_user\")\n",
    "        bias_item = tf.nn.embedding_lookup(w_bias_item, item_batch, name=\"bias_item\")\n",
    "        w_user = tf.get_variable(\"embd_user\", shape=[user_num, dim],\n",
    "                                 initializer=tf.truncated_normal_initializer(stddev=0.02))\n",
    "        w_item = tf.get_variable(\"embd_item\", shape=[item_num, dim],\n",
    "                                 initializer=tf.truncated_normal_initializer(stddev=0.02))\n",
    "        # user向量与item向量\n",
    "        embd_user = tf.nn.embedding_lookup(w_user, user_batch, name=\"embedding_user\")\n",
    "        embd_item = tf.nn.embedding_lookup(w_item, item_batch, name=\"embedding_item\")\n",
    "    with tf.device(device):\n",
    "        # 按照实际公式进行计算\n",
    "        # 先对user向量和item向量求内积\n",
    "        infer = tf.reduce_sum(tf.multiply(embd_user, embd_item), 1)\n",
    "        # 加上几个偏置项\n",
    "        infer = tf.add(infer, global_bias)\n",
    "        infer = tf.add(infer, bias_user)\n",
    "        infer = tf.add(infer, bias_item, name=\"svd_inference\")\n",
    "        # 加上正则化项\n",
    "        regularizer = tf.add(tf.nn.l2_loss(embd_user), tf.nn.l2_loss(embd_item), name=\"svd_regularizer\")\n",
    "    return infer, regularizer\n",
    "\n",
    "# 迭代优化部分\n",
    "def optimization(infer, regularizer, rate_batch, learning_rate=0.001, reg=0.1, device=\"/cpu:0\"):\n",
    "    global_step = tf.train.get_global_step()\n",
    "    assert global_step is not None\n",
    "    # 选择合适的optimizer做优化\n",
    "    with tf.device(device):\n",
    "        cost_l2 = tf.nn.l2_loss(tf.subtract(infer, rate_batch))\n",
    "        penalty = tf.constant(reg, dtype=tf.float32, shape=[], name=\"l2\")\n",
    "        cost = tf.add(cost_l2, tf.multiply(regularizer, penalty))\n",
    "        train_op = tf.train.AdamOptimizer(learning_rate).minimize(cost, global_step=global_step)\n",
    "    return cost, train_op"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import time\n",
    "from collections import deque\n",
    "\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from six import next\n",
    "from tensorflow.core.framework import summary_pb2\n",
    "\n",
    "np.random.seed(13575)\n",
    "\n",
    "# 一批数据的大小\n",
    "BATCH_SIZE = 2000\n",
    "# 用户数\n",
    "USER_NUM = 6040\n",
    "# 电影数\n",
    "ITEM_NUM = 3952\n",
    "# factor维度\n",
    "DIM = 15\n",
    "# 最大迭代轮数\n",
    "EPOCH_MAX = 200\n",
    "# 使用cpu做训练\n",
    "DEVICE = \"/cpu:0\"\n",
    "\n",
    "# 截断\n",
    "def clip(x):\n",
    "    return np.clip(x, 1.0, 5.0)\n",
    "\n",
    "# 这个是方便Tensorboard可视化做的summary\n",
    "def make_scalar_summary(name, val):\n",
    "    return summary_pb2.Summary(value=[summary_pb2.Summary.Value(tag=name, simple_value=val)])\n",
    "\n",
    "# 调用上面的函数获取数据\n",
    "def get_data():\n",
    "    df = read_data_and_process(\"./movielens/ml-1m/ratings.dat\", sep=\"::\")\n",
    "    rows = len(df)\n",
    "    df = df.iloc[np.random.permutation(rows)].reset_index(drop=True)\n",
    "    split_index = int(rows * 0.9)\n",
    "    df_train = df[0:split_index]\n",
    "    df_test = df[split_index:].reset_index(drop=True)\n",
    "    print(df_train.shape, df_test.shape)\n",
    "    return df_train, df_test\n",
    "\n",
    "# 实际训练过程\n",
    "def svd(train, test):\n",
    "    samples_per_batch = len(train) // BATCH_SIZE\n",
    "\n",
    "    # 一批一批数据用于训练\n",
    "    iter_train = ShuffleDataIterator([train[\"user\"],\n",
    "                                         train[\"item\"],\n",
    "                                         train[\"rate\"]],\n",
    "                                        batch_size=BATCH_SIZE)\n",
    "    # 测试数据\n",
    "    iter_test = OneEpochDataIterator([test[\"user\"],\n",
    "                                         test[\"item\"],\n",
    "                                         test[\"rate\"]],\n",
    "                                        batch_size=-1)\n",
    "    # user和item batch\n",
    "    user_batch = tf.placeholder(tf.int32, shape=[None], name=\"id_user\")\n",
    "    item_batch = tf.placeholder(tf.int32, shape=[None], name=\"id_item\")\n",
    "    rate_batch = tf.placeholder(tf.float32, shape=[None])\n",
    "\n",
    "    # 构建graph和训练\n",
    "    infer, regularizer = inference_svd(user_batch, item_batch, user_num=USER_NUM, item_num=ITEM_NUM, dim=DIM,\n",
    "                                           device=DEVICE)\n",
    "    global_step = tf.contrib.framework.get_or_create_global_step()\n",
    "    _, train_op = optimization(infer, regularizer, rate_batch, learning_rate=0.001, reg=0.05, device=DEVICE)\n",
    "\n",
    "    # 初始化所有变量\n",
    "    init_op = tf.global_variables_initializer()\n",
    "    # 开始迭代\n",
    "    with tf.Session() as sess:\n",
    "        sess.run(init_op)\n",
    "        summary_writer = tf.summary.FileWriter(logdir=\"/tmp/svd/log\", graph=sess.graph)\n",
    "        print(\"{} {} {} {}\".format(\"epoch\", \"train_error\", \"val_error\", \"elapsed_time\"))\n",
    "        errors = deque(maxlen=samples_per_batch)\n",
    "        start = time.time()\n",
    "        for i in range(EPOCH_MAX * samples_per_batch):\n",
    "            users, items, rates = next(iter_train)\n",
    "            _, pred_batch = sess.run([train_op, infer], feed_dict={user_batch: users,\n",
    "                                                                   item_batch: items,\n",
    "                                                                   rate_batch: rates})\n",
    "            pred_batch = clip(pred_batch)\n",
    "            errors.append(np.power(pred_batch - rates, 2))\n",
    "            if i % samples_per_batch == 0:\n",
    "                train_err = np.sqrt(np.mean(errors))\n",
    "                test_err2 = np.array([])\n",
    "                for users, items, rates in iter_test:\n",
    "                    pred_batch = sess.run(infer, feed_dict={user_batch: users,\n",
    "                                                            item_batch: items})\n",
    "                    pred_batch = clip(pred_batch)\n",
    "                    test_err2 = np.append(test_err2, np.power(pred_batch - rates, 2))\n",
    "                end = time.time()\n",
    "                test_err = np.sqrt(np.mean(test_err2))\n",
    "                print(\"{:3d} {:f} {:f} {:f}(s)\".format(i // samples_per_batch, train_err, test_err,\n",
    "                                                       end - start))\n",
    "                train_err_summary = make_scalar_summary(\"training_error\", train_err)\n",
    "                test_err_summary = make_scalar_summary(\"test_error\", test_err)\n",
    "                summary_writer.add_summary(train_err_summary, i)\n",
    "                summary_writer.add_summary(test_err_summary, i)\n",
    "                start = end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(900188, 4) (100021, 4)\n"
     ]
    }
   ],
   "source": [
    "# 获取数据\n",
    "df_train, df_test = get_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda\\install\\lib\\site-packages\\h5py\\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch train_error val_error elapsed_time\n",
      "  0 2.576278 2.577729 0.477677(s)\n",
      "  1 1.978902 1.152332 1.450331(s)\n",
      "  2 1.002632 0.949393 1.423475(s)\n",
      "  3 0.927719 0.926508 1.450366(s)\n",
      "  4 0.914275 0.919153 1.534211(s)\n",
      "  5 0.910865 0.915688 1.624905(s)\n",
      "  6 0.906089 0.913335 1.514213(s)\n",
      "  7 0.904977 0.911318 1.424107(s)\n",
      "  8 0.901721 0.908855 1.496397(s)\n",
      "  9 0.896913 0.906264 1.611612(s)\n",
      " 10 0.894468 0.903484 1.795376(s)\n",
      " 11 0.891712 0.899968 1.503599(s)\n",
      " 12 0.887555 0.895848 1.421528(s)\n",
      " 13 0.882009 0.891982 1.420877(s)\n",
      " 14 0.876975 0.888060 1.567020(s)\n",
      " 15 0.872943 0.884968 1.627547(s)\n",
      " 16 0.867226 0.881633 1.486202(s)\n",
      " 17 0.864066 0.878666 1.431954(s)\n",
      " 18 0.859931 0.875910 1.438102(s)\n",
      " 19 0.856037 0.873030 1.433395(s)\n",
      " 20 0.849924 0.870667 1.421893(s)\n",
      " 21 0.846303 0.868094 1.397581(s)\n",
      " 22 0.842261 0.865835 1.398386(s)\n",
      " 23 0.836717 0.863661 1.395191(s)\n",
      " 24 0.833121 0.861465 1.390639(s)\n",
      " 25 0.829651 0.859585 1.461942(s)\n",
      " 26 0.824811 0.857843 1.403177(s)\n",
      " 27 0.820917 0.856483 1.398302(s)\n",
      " 28 0.816505 0.854711 1.390662(s)\n",
      " 29 0.813360 0.853433 1.402261(s)\n",
      " 30 0.808135 0.852419 1.468770(s)\n",
      " 31 0.805145 0.851025 1.394093(s)\n",
      " 32 0.799418 0.849873 1.406268(s)\n",
      " 33 0.797527 0.849210 1.390641(s)\n",
      " 34 0.794350 0.848693 1.399524(s)\n",
      " 35 0.792427 0.848298 1.447494(s)\n",
      " 36 0.789376 0.847890 1.403100(s)\n",
      " 37 0.786277 0.847480 1.406268(s)\n",
      " 38 0.783722 0.847279 1.392901(s)\n",
      " 39 0.781859 0.846988 1.433542(s)\n",
      " 40 0.779194 0.846766 1.460803(s)\n",
      " 41 0.776687 0.846418 1.502838(s)\n",
      " 42 0.774345 0.846484 1.477898(s)\n",
      " 43 0.773097 0.846666 1.470419(s)\n",
      " 44 0.772025 0.846828 1.406287(s)\n",
      " 45 0.769199 0.846732 1.445719(s)\n",
      " 46 0.768910 0.846695 1.390641(s)\n",
      " 47 0.766496 0.846699 1.395308(s)\n",
      " 48 0.765846 0.846611 1.407348(s)\n",
      " 49 0.764256 0.846703 1.406266(s)\n",
      " 50 0.762772 0.846718 1.446595(s)\n",
      " 51 0.761644 0.847029 1.390661(s)\n",
      " 52 0.760738 0.847263 1.413254(s)\n",
      " 53 0.759950 0.847614 1.419673(s)\n",
      " 54 0.759713 0.847827 1.400326(s)\n",
      " 55 0.757802 0.847982 1.421893(s)\n",
      " 56 0.757559 0.848026 1.437567(s)\n",
      " 57 0.757013 0.848383 1.414295(s)\n",
      " 58 0.756566 0.848557 1.390639(s)\n",
      " 59 0.756866 0.848483 1.413066(s)\n",
      " 60 0.753830 0.848556 1.406267(s)\n",
      " 61 0.754405 0.848785 1.447202(s)\n",
      " 62 0.754867 0.848690 1.390621(s)\n",
      " 63 0.753079 0.848909 1.416439(s)\n",
      " 64 0.753559 0.848946 1.383843(s)\n",
      " 65 0.753118 0.849353 1.399104(s)\n",
      " 66 0.751364 0.849349 1.481309(s)\n",
      " 67 0.752177 0.849697 1.449126(s)\n",
      " 68 0.751095 0.849683 1.468768(s)\n",
      " 69 0.751063 0.849502 1.383999(s)\n",
      " 70 0.750350 0.849622 1.406266(s)\n",
      " 71 0.751395 0.849533 1.446464(s)\n",
      " 72 0.750082 0.849392 1.400997(s)\n",
      " 73 0.750379 0.849434 1.388548(s)\n",
      " 74 0.749501 0.849552 1.407498(s)\n",
      " 75 0.750194 0.849896 1.461215(s)\n",
      " 76 0.750201 0.849961 1.446918(s)\n",
      " 77 0.749083 0.850167 1.404643(s)\n",
      " 78 0.750445 0.850135 1.404541(s)\n",
      " 79 0.749501 0.849938 1.453143(s)\n",
      " 80 0.747849 0.850081 1.394538(s)\n",
      " 81 0.747658 0.850377 1.500019(s)\n",
      " 82 0.747445 0.850573 1.417488(s)\n",
      " 83 0.748725 0.850522 1.484394(s)\n",
      " 84 0.748016 0.850637 1.407718(s)\n",
      " 85 0.746435 0.850938 1.380105(s)\n",
      " 86 0.747316 0.850969 1.448309(s)\n",
      " 87 0.746777 0.850801 1.406286(s)\n",
      " 88 0.746731 0.850807 1.400060(s)\n",
      " 89 0.747924 0.850830 1.385019(s)\n",
      " 90 0.746106 0.850674 1.400585(s)\n",
      " 91 0.746864 0.850689 1.419417(s)\n",
      " 92 0.746962 0.850772 1.461914(s)\n",
      " 93 0.746395 0.850632 1.400366(s)\n",
      " 94 0.746491 0.850653 1.390425(s)\n",
      " 95 0.746701 0.850703 1.469709(s)\n",
      " 96 0.745090 0.850457 1.413338(s)\n",
      " 97 0.745649 0.850722 1.436355(s)\n",
      " 98 0.745338 0.850862 1.437517(s)\n",
      " 99 0.745499 0.850813 1.481134(s)\n",
      "100 0.745503 0.850798 1.374998(s)\n",
      "101 0.745268 0.850891 1.413138(s)\n",
      "102 0.745327 0.850786 1.476407(s)\n",
      "103 0.746660 0.850860 1.468770(s)\n",
      "104 0.745549 0.851016 1.390663(s)\n",
      "105 0.744760 0.850981 1.399773(s)\n",
      "106 0.745388 0.850703 1.395961(s)\n",
      "107 0.745142 0.850666 1.462982(s)\n",
      "108 0.746368 0.850706 1.390665(s)\n",
      "109 0.744704 0.850997 1.406251(s)\n",
      "110 0.745588 0.850987 1.399718(s)\n",
      "111 0.743731 0.851158 1.598807(s)\n",
      "112 0.744651 0.851077 1.611395(s)\n",
      "113 0.744472 0.850991 1.437518(s)\n",
      "114 0.744883 0.851003 1.408719(s)\n",
      "115 0.744321 0.850906 1.415562(s)\n",
      "116 0.744179 0.851158 1.406284(s)\n",
      "117 0.744853 0.851024 1.486450(s)\n",
      "118 0.743401 0.850973 1.420012(s)\n",
      "119 0.744809 0.851009 1.399587(s)\n",
      "120 0.744726 0.851097 1.390638(s)\n",
      "121 0.743952 0.850803 1.446391(s)\n",
      "122 0.744973 0.850798 1.439853(s)\n",
      "123 0.744382 0.850887 1.431332(s)\n",
      "124 0.744419 0.850841 1.460428(s)\n",
      "125 0.743825 0.851252 1.468669(s)\n",
      "126 0.744768 0.850956 1.406268(s)\n",
      "127 0.743264 0.850907 1.462467(s)\n",
      "128 0.743480 0.850931 1.390640(s)\n",
      "129 0.743621 0.851018 1.391578(s)\n",
      "130 0.744046 0.850966 1.407784(s)\n",
      "131 0.743349 0.850969 1.390645(s)\n",
      "132 0.743841 0.850785 1.462579(s)\n",
      "133 0.743074 0.850948 1.458358(s)\n",
      "134 0.744358 0.850806 1.407487(s)\n",
      "135 0.743972 0.851127 1.390640(s)\n",
      "136 0.743227 0.851148 1.406265(s)\n",
      "137 0.742984 0.851232 1.447071(s)\n",
      "138 0.744403 0.851532 1.411106(s)\n",
      "139 0.743451 0.851401 1.469621(s)\n",
      "140 0.743391 0.851384 1.390645(s)\n",
      "141 0.744516 0.851492 1.406285(s)\n",
      "142 0.743470 0.851447 1.410015(s)\n",
      "143 0.743198 0.851322 1.420781(s)\n",
      "144 0.744412 0.851270 1.403787(s)\n",
      "145 0.742384 0.851284 1.390643(s)\n",
      "146 0.743339 0.851364 1.399891(s)\n",
      "147 0.742802 0.851247 1.395700(s)\n",
      "148 0.742878 0.851421 1.469909(s)\n",
      "149 0.743484 0.851321 1.399727(s)\n",
      "150 0.743502 0.851572 1.399970(s)\n",
      "151 0.743406 0.851571 1.399521(s)\n",
      "152 0.742925 0.851396 1.395897(s)\n",
      "153 0.742553 0.851295 1.451918(s)\n",
      "154 0.743613 0.851278 1.406268(s)\n",
      "155 0.741762 0.851363 1.525129(s)\n",
      "156 0.743210 0.851457 1.406286(s)\n",
      "157 0.743032 0.851381 1.395900(s)\n",
      "158 0.741658 0.851501 1.455009(s)\n",
      "159 0.743116 0.851250 1.406258(s)\n",
      "160 0.743059 0.851320 1.399649(s)\n",
      "161 0.743155 0.851130 1.406287(s)\n",
      "162 0.741716 0.851186 1.395491(s)\n",
      "163 0.742589 0.851172 1.439167(s)\n",
      "164 0.742117 0.850974 1.468770(s)\n",
      "165 0.742390 0.851116 1.409128(s)\n",
      "166 0.744096 0.851254 1.435538(s)\n",
      "167 0.742634 0.851376 1.388916(s)\n",
      "168 0.741646 0.851275 1.457746(s)\n",
      "169 0.742897 0.851177 1.366868(s)\n",
      "170 0.743052 0.851266 1.426625(s)\n",
      "171 0.742376 0.851271 1.403697(s)\n",
      "172 0.742742 0.851338 1.390625(s)\n",
      "173 0.742256 0.851197 1.453141(s)\n",
      "174 0.742268 0.851046 1.399479(s)\n",
      "175 0.742002 0.850905 1.383932(s)\n",
      "176 0.741890 0.851078 1.421894(s)\n",
      "177 0.743085 0.851033 1.406270(s)\n",
      "178 0.741955 0.850917 1.416128(s)\n",
      "179 0.742171 0.851150 1.442452(s)\n",
      "180 0.742805 0.851376 1.423072(s)\n",
      "181 0.741585 0.851463 1.437518(s)\n",
      "182 0.742271 0.851443 1.437522(s)\n",
      "183 0.743029 0.851565 1.461741(s)\n",
      "184 0.742284 0.851456 1.504725(s)\n",
      "185 0.741653 0.851479 1.454393(s)\n",
      "186 0.743889 0.851576 1.564915(s)\n",
      "187 0.742872 0.851446 1.650403(s)\n",
      "188 0.741979 0.851394 1.431463(s)\n",
      "189 0.742107 0.851101 1.421539(s)\n",
      "190 0.742485 0.851297 1.406282(s)\n",
      "191 0.740788 0.851228 1.415401(s)\n",
      "192 0.742113 0.851329 1.414911(s)\n",
      "193 0.741579 0.851133 1.462146(s)\n",
      "194 0.742999 0.851144 1.455878(s)\n",
      "195 0.742513 0.851250 1.415932(s)\n",
      "196 0.743028 0.851395 1.390641(s)\n",
      "197 0.742302 0.851131 1.579862(s)\n",
      "198 0.741136 0.851173 1.607915(s)\n",
      "199 0.741375 0.851128 1.723560(s)\n"
     ]
    }
   ],
   "source": [
    "# 完成实际的训练\n",
    "svd(df_train, df_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
